package com.hhd.flow.engine.utils;

import com.hhd.flow.engine.FlowEngine;
import com.hhd.flow.engine.cmd.PassTaskContext;
import com.hhd.flow.engine.cmd.impl.*;
import com.hhd.flow.engine.cmd.impl.virtual.VirtualStartCommand;
import com.hhd.flow.engine.lisener.event.*;
import com.hhd.flow.engine.vo.*;
import com.hhd.flow.entity.*;

import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

public class FlowUtils {
    public static FlowTask createStartTask(FlowProcessInstance flowProcessInstance) {
        FlowTask flowTask = new FlowTask();
        flowTask.setTaskName("开始");
        flowTask.setType("start");
        flowTask.setTaskDefinitionId("start");
        flowTask.setProcessDefinitionId(flowProcessInstance.getProcessDefinitionId());
        flowTask.setProcessInstanceId(flowProcessInstance.getId());
        return flowTask;
    }

    public static FlowProcessInstance newInstanceForModel(FlowModel flowModel, VirtualStartCommand command) {
        FlowProcessInstance flowProcessInstance = new FlowProcessInstance();
        flowProcessInstance.setProcessDefinitionId(command.getProcessDefinitionId());
        flowProcessInstance.setFormTypeId(command.getFormTypeId());
        flowProcessInstance.setFormId(command.getFormId());
        flowProcessInstance.setApplyBy(command.getApplyBy().getId());
        flowProcessInstance.setApplyByName(command.getApplyBy().getUsername());
        flowProcessInstance.setFinished(false);
        flowProcessInstance.setApproved(false);
        return flowProcessInstance;
    }

    public static FlowProcessInstance newInstanceForModel(FlowModel model, StartProcessCommand command) {
        FlowProcessInstance flowProcessInstance = new FlowProcessInstance();
        flowProcessInstance.setProcessDefinitionId(command.getProcessDefinitionId());
        flowProcessInstance.setFormTypeId(command.getFormTypeId());
        flowProcessInstance.setFormId(command.getFormId());
        flowProcessInstance.setApplyBy(command.getApplyBy().getId());
        flowProcessInstance.setApplyByName(command.getApplyBy().getUsername());
        flowProcessInstance.setFinished(false);
        flowProcessInstance.setApproved(false);
        return flowProcessInstance;
    }


    public static FlowPath createFlowPath(FlowProcessInstance flowProcessInstance) {
        FlowPath flowPath = new FlowPath();
        flowPath.setProcessInstanceId(flowProcessInstance.getId());
        flowPath.setPath("");
        return flowPath;
    }

    public static FlowTask createFlowTask(UserTask userTask, List<FlowUser> flowUsers, PassTaskContext context) {
        FlowTask flowTask = new FlowTask();
        flowTask.setTaskName(userTask.getTaskName());
        flowTask.setType(userTask.getType());
        flowTask.setTaskDefinitionId(userTask.getId());
        flowTask.setProcessDefinitionId(context.getFlowProcessInstance().getProcessDefinitionId());
        flowTask.setProcessInstanceId(context.getFlowProcessInstance().getId());
        flowTask.setExecuteWay(userTask.getExecuteWay().name());
        flowTask.setExecuteCount(0);
        flowTask.setUserIds(flowUsers.stream().map(FlowUser::getId).collect(Collectors.joining(",")));
        flowTask.setUserNames(flowUsers.stream().map(FlowUser::getUsername).collect(Collectors.joining(",")));
        flowTask.setUserTaskType(userTask.getUserTaskType());
        //如果单用户审批返回了多个用户，那就转换成多用户竞争
        if (userTask.getExecuteWay() == UserTask.ExecuteWay.single) {
            flowTask.setExecuteThreshold(1);
            flowTask.setExecuteWay(UserTask.ExecuteWay.multiCompetition.name());
        } else if (userTask.getExecuteWay() == UserTask.ExecuteWay.multiCompetition) {
            flowTask.setExecuteThreshold(1);
            flowTask.setExecuteWay(UserTask.ExecuteWay.multiCompetition.name());
        } else {
            flowTask.setExecuteThreshold(createExecuteThreshold(userTask, flowUsers));
            flowTask.setExecuteWay(userTask.getExecuteWay().name());
        }
        //如果当前任务是一个知会任务, 那就直接把通过阈值设置成0, 下次流转就会自动通过
        if (userTask.isNotify()) {
            flowTask.setExecuteThreshold(0);
        }

        return flowTask;
    }

    private static Integer createExecuteThreshold(UserTask userTask, List<FlowUser> flowUsers) {
        return Math.min(flowUsers.size() * userTask.getExecuteThreshold() / 100 + 1, flowUsers.size());
    }

    public static List<FlowUserTask> createFlowUserTasks(FlowTask flowTask, List<FlowUser> flowUsers, FlowProcessInstance flowProcessInstance, UserTask userTask) {
        return flowUsers.stream().map(user -> createFlowUserTask(flowTask, user, flowProcessInstance, userTask)).collect(Collectors.toList());
    }

    public static FlowUserTask createFlowUserTask(FlowTask flowTask, FlowUser user, FlowProcessInstance flowProcessInstance, UserTask userTask) {
        FlowUserTask flowUserTask = new FlowUserTask();
        flowUserTask.setTaskName(flowTask.getTaskName());
        flowUserTask.setUserId(user.getId());
        flowUserTask.setUsername(user.getUsername());
        flowUserTask.setTaskInstanceId(flowTask.getId());
        flowUserTask.setTaskDefinitionId(flowTask.getTaskDefinitionId());
        flowUserTask.setProcessInstanceId(flowTask.getProcessInstanceId());
        flowUserTask.setProcessDefinitionId(flowTask.getProcessDefinitionId());
        flowUserTask.setFormTypeId(flowProcessInstance.getFormTypeId());
        flowUserTask.setFormId(flowProcessInstance.getFormId());
        flowUserTask.setProcessTitle(flowProcessInstance.getProcessTitle());
        flowUserTask.setApplyBy(flowProcessInstance.getApplyBy());
        flowUserTask.setApplyByName(flowProcessInstance.getApplyByName());

        flowUserTask.setPcView(userTask.getPcView());
        flowUserTask.setPcAction(userTask.getPcAction());
        flowUserTask.setMobileView(userTask.getMobileView());
        flowUserTask.setMobileAction(userTask.getMobileAction());

        flowUserTask.setMustSignature(userTask.isMustSignature());

        flowUserTask.setActionAuth(userTask.getActionAuth());

        flowUserTask.setBeforeCompleteListener(userTask.getEveryBeforeComplete());
        flowUserTask.setAfterCompleteListener(userTask.getEveryAfterComplete());

        flowUserTask.setEveryBeforeCompleteListener(userTask.getEveryBeforeComplete());
        flowUserTask.setEveryAfterCompleteListener(userTask.getEveryAfterComplete());

        flowUserTask.setSupportAutoComplete(userTask.isAutoComplete());
        flowUserTask.setNotify(false);
        flowUserTask.setActive(true);
        flowUserTask.setFromCountSign(false);
        flowUserTask.setTaskKey(userTask.getTaskKey());
        flowUserTask.setUserTaskType(userTask.getUserTaskType());

        if (userTask.isNotify()) {
            flowUserTask.setNotify(true);
        }

        if (userTask.isAddCountSign()) {
            flowUserTask.setFromCountSign(true);
            flowUserTask.setCountFrom(userTask.getCountFrom());
            flowUserTask.setCountFromUsername(userTask.getCountFromUsername());
            flowUserTask.setCountFromTaskDefinitionId(userTask.getCountFromTaskDefinitionId());
            flowUserTask.setCountFromTaskId(userTask.getCountFromTaskId());
        }

        if (CommonUtils.isEmpty(flowUserTask.getActionAuth())) {
            flowUserTask.setActionAuth("default");
        }

        return flowUserTask;
    }

    public static FlowComment createComment(FlowUserTask flowUserTask, String comment, String userSign, String attachmentIds, ApproveType approveType, String commentInfo) {
        FlowComment flowComment = new FlowComment();
        flowComment.setTaskName(flowUserTask.getTaskName());
        flowComment.setUserId(flowUserTask.getUserId());
        flowComment.setUsername(flowUserTask.getUsername());
        flowComment.setTaskKey(flowUserTask.getTaskKey());

        flowComment.setTaskInstanceId(flowUserTask.getTaskInstanceId());
        flowComment.setTaskDefinitionId(flowUserTask.getTaskDefinitionId());

        flowComment.setProcessInstanceId(flowUserTask.getProcessInstanceId());
        flowComment.setProcessDefinitionId(flowUserTask.getProcessDefinitionId());

        flowComment.setMessage(comment);
        flowComment.setAttachmentIds(attachmentIds);

        flowComment.setCommentType(approveType.name());
        flowComment.setCommentTypeName(approveType.getTitle());
        flowComment.setCommentInfo(commentInfo);
        flowComment.setUserTaskId(flowUserTask.getUserId());

        flowComment.setFormId(flowUserTask.getFormId());
        flowComment.setUserSign(userSign);
        return flowComment;
    }

    public static FlowComment createComment(FlowUserTask flowUserTask, String comment, String userSign, String attachmentIds, CommentType commentType, String commentInfo) {
        FlowComment flowComment = createComment(flowUserTask, comment, userSign, attachmentIds, ApproveType.approve, commentInfo);
        flowComment.setCommentType(commentType.name());
        flowComment.setCommentTypeName(commentType.getTitle());
        return flowComment;
    }


    public static FlowTask createStartGatewayTask(Gateway gateway, FlowProcessInstance flowProcessInstance) {
        FlowTask flowTask = new FlowTask();
        flowTask.setTaskName("开始网关:" + gateway.getId());
        flowTask.setType(gateway.getType());
        flowTask.setTaskDefinitionId(gateway.getId());
        flowTask.setProcessDefinitionId(flowProcessInstance.getProcessDefinitionId());
        flowTask.setProcessInstanceId(flowProcessInstance.getId());
        return flowTask;
    }

    public static FlowGatewayExecution createGateWayExecution(FlowTask flowTask, Gateway gateway, PassTaskContext context, int executeThreshold) {
        FlowGatewayExecution flowGatewayExecution = new FlowGatewayExecution();
        Gateway endGateway = gateway.getEndGateway(context.getFlowModel());
        flowGatewayExecution.setStartGatewayDefinitionId(gateway.getId());
        flowGatewayExecution.setEndGatewayDefinitionId(endGateway.getId());
        flowGatewayExecution.setExecuteCount(0);
        flowGatewayExecution.setExecuteThreshold(executeThreshold);
        flowGatewayExecution.setTaskInstanceId(flowTask.getId());
        flowGatewayExecution.setProcessInstanceId(context.getFlowProcessInstance().getId());
        flowGatewayExecution.setProcessDefinitionId(context.getFlowProcessInstance().getProcessDefinitionId());
        return flowGatewayExecution;
    }

    public static FlowTask createEndGatewayTask(Gateway endGateway, FlowProcessInstance flowProcessInstance) {
        FlowTask flowTask = new FlowTask();
        flowTask.setTaskName("结束网关:" + endGateway.getId());
        flowTask.setType(endGateway.getType());
        flowTask.setTaskDefinitionId(endGateway.getId());
        flowTask.setProcessDefinitionId(flowProcessInstance.getProcessDefinitionId());
        flowTask.setProcessInstanceId(flowProcessInstance.getId());
        flowTask.setExecuteCount(0);
        flowTask.setExecuteThreshold(0);
        return flowTask;
    }

    public static FlowTask createEmptyTask(FlowNode flowNode, FlowProcessInstance flowProcessInstance) {
        FlowTask flowTask = new FlowTask();
        flowTask.setTaskName("空节点");
        flowTask.setType(flowNode.getType().name());
        flowTask.setTaskDefinitionId(flowNode.getId());
        flowTask.setProcessDefinitionId(flowProcessInstance.getProcessDefinitionId());
        flowTask.setProcessInstanceId(flowProcessInstance.getId());
        return flowTask;
    }

    public static void endFlowUserTask(FlowUserTask flowUserTask, FlowEnum approveType, String userSign) {
        if (!CommonUtils.isEmpty(approveType)) {
            flowUserTask.setApproveType(approveType.name());
            flowUserTask.setApproveTypeName(approveType.getTitle());
        }
        flowUserTask.setActive(false);
        flowUserTask.setCompleteTime(new Date());
        flowUserTask.setUserSign(userSign);
    }

    public static FlowProcessInstanceHistory createFlowProcessInstanceHistory(FlowProcessInstance processInstance, FlowEnum approveType, Boolean approved) {
        FlowProcessInstanceHistory flowProcessInstanceHistory = CommonUtils.copyTo(processInstance, FlowProcessInstanceHistory.class);
        if (!CommonUtils.isEmpty(approveType)) {
            flowProcessInstanceHistory.setApprovedType(approveType.name());
            flowProcessInstanceHistory.setApprovedTypeName(approveType.getTitle());
        }
        flowProcessInstanceHistory.setApproved(approved);
        flowProcessInstanceHistory.setEndTime(new Date());
        return flowProcessInstanceHistory;
    }

    public static List<FlowUser> getAddCountSignUsers(UserTask userTask) {
        String[] addCountSignUsers = userTask.getAddCountSignUsers().split(",");
        String[] addCountSignUserNames = userTask.getAddCountSignUserNames().split(",");
        List<FlowUser> res = CommonUtils.newList();
        for (int i = 0; i < addCountSignUsers.length; i++) {
            res.add(new FlowUser(addCountSignUsers[i], addCountSignUserNames[i]));
        }
        return res;
    }

    public static FlowUserDone createFlowUserDone(FlowUserTaskHistory flowUserTaskHistory) {
        return CommonUtils.copyTo(flowUserTaskHistory, FlowUserDone.class);
    }

    public static StartProcessBeforeEvent createStartProcessBeforeEvent(StartProcessCommand startProcessCommand) {
        StartProcessBeforeEvent event = new StartProcessBeforeEvent();
        event.setStartProcessCommand(startProcessCommand);
        return event;
    }

    public static StartProcessAfterEvent createStartProcessAfterEvent(PassTaskContext context, StartProcessCommand startProcessCommand) {
        StartProcessAfterEvent event = new StartProcessAfterEvent();
        event.setContext(context);
        event.setStartProcessCommand(startProcessCommand);
        return event;
    }

    public static BeforeEnterUserTaskEvent createBeforeEnterUserTaskEvent(UserTask userTask, PassTaskContext context, List<FlowUser> flowUsers) {
        BeforeEnterUserTaskEvent event = new BeforeEnterUserTaskEvent();
        event.setUserTask(userTask);
        event.setFlowUsers(flowUsers);
        event.setContext(context);
        return event;
    }

    public static AfterEnterUserTaskEvent createAfterEnterUserTaskEvent(UserTask userTask, PassTaskContext context, List<FlowUser> flowUsers, FlowTask flowTask, List<FlowUserTask> flowUserTasks) {
        AfterEnterUserTaskEvent event = new AfterEnterUserTaskEvent();
        event.setUserTask(userTask);
        event.setContext(context);
        event.setFlowUsers(flowUsers);
        event.setFlowTask(flowTask);
        event.setFlowUserTasks(flowUserTasks);
        return event;
    }

    public static UserTaskCompleteEvent createUserTaskCompleteEvent(FlowNode flowNode, FlowTask flowTask, PassTaskContext context) {
        UserTaskCompleteEvent event = new UserTaskCompleteEvent();
        event.setContext(context);
        event.setFlowNode(flowNode);
        event.setFlowTask(flowTask);
        return event;
    }

    public static UserTaskCompleteEveryEvent createUserTaskCompleteEveryEvent(CompleteUserTaskCommand completeUserTaskCommand) {
        UserTaskCompleteEveryEvent event = new UserTaskCompleteEveryEvent();
        event.setCompleteUserTaskCommand(completeUserTaskCommand);
        return event;
    }

    public static ProcessApprovedEvent createProcessApprovedEvent(PassTaskContext context) {
        ProcessApprovedEvent event = new ProcessApprovedEvent();
        event.setContext(context);
        return event;
    }

    public static FlowUserTaskEndEvent createFlowUserTaskEndEvent(EndCommand endCommand) {
        FlowUserTaskEndEvent event = new FlowUserTaskEndEvent();
        event.setEndCommand(endCommand);
        return event;
    }

    public static FlowUserTaskToFormEvent createFlowUserTaskToFormEvent(ToFormCommand toFormCommand) {
        FlowUserTaskToFormEvent event = new FlowUserTaskToFormEvent();
        event.setToFormCommand(toFormCommand);
        return event;
    }

    public static EnterNodeEvent createEnterNodeEvent(List<FlowTask> flowTasks, FlowNode flowNode, PassTaskContext context) {
        EnterNodeEvent event = new EnterNodeEvent();
        event.setContext(context);
        event.setFlowNode(flowNode);
        event.setFlowTasks(flowTasks);
        return event;
    }

    public static PassNodeEvent createPassNodeEvent(FlowTask flowTask, FlowNode flowNode, PassTaskContext context) {
        PassNodeEvent event = new PassNodeEvent();
        event.setContext(context);
        event.setFlowNode(flowNode);
        event.setFlowTask(flowTask);
        return event;
    }

    public static ProcessFinishEvent createProcessFinishEvent(FlowProcessInstance flowProcessInstance, ApproveType approveType, FlowModel flowModel) {
        ProcessFinishEvent event = new ProcessFinishEvent();
        event.setFlowProcessInstance(flowProcessInstance);
        event.setApproveType(approveType);
        event.setFlowModel(flowModel);
        return event;
    }

    public static CreateUserTaskEvent createCreateUserTaskEvent(List<FlowUserTask> flowUserTasks, FlowProcessInstance flowProcessInstance) {
        CreateUserTaskEvent event = new CreateUserTaskEvent();
        event.setFlowProcessInstance(flowProcessInstance);
        event.setFlowUserTasks(flowUserTasks);
        return event;
    }

    public static UserNodeSelect createSelectOption(List<FlowUser> flowUsers, UserTask userTask, PassTaskContext context) {
        UserNodeSelect selectOption = new UserNodeSelect();
        selectOption.setNodeId(userTask.getId());
        selectOption.setNodeName(userTask.getTaskName());
        selectOption.setUsers(flowUsers);

        String reason = userTask.getUserMenuSelectReason();
        if (CommonUtils.isEmpty(reason)) {
            reason = "请选择下一个节点的审批人";
        }
        selectOption.setUserMenuSelectReason(reason);
        selectOption.setMultiple(false);
        if (userTask.getExecuteWay() == UserTask.ExecuteWay.multiCompetition
        || userTask.getExecuteWay() == UserTask.ExecuteWay.multiSequence
                || userTask.getExecuteWay() == UserTask.ExecuteWay.multi) {
            selectOption.setMultiple(true);
        }
        return selectOption;
    }

    public static FlowComment createFlowComment(FlowProcessInstance flowProcessInstance) {
        FlowComment comment = new FlowComment();
        comment.setTaskName("启动流程");
        comment.setUserId(flowProcessInstance.getApplyBy());
        comment.setUsername(flowProcessInstance.getApplyByName());
        comment.setUserTaskId(flowProcessInstance.getId());
        comment.setTaskInstanceId(flowProcessInstance.getId());
        comment.setTaskInstanceId("start");
        comment.setTaskDefinitionId("start");
        comment.setProcessInstanceId(flowProcessInstance.getId());
        comment.setProcessDefinitionId(flowProcessInstance.getProcessDefinitionId());
        comment.setCommentType("start");
        comment.setCommentTypeName("提交表单");
        comment.setFormId(flowProcessInstance.getFormId());
        return comment;
    }

    public static FlowComment createWithdrawComment(FlowProcessInstance flowProcessInstance) {
        FlowComment comment = new FlowComment();
        comment.setTaskName("撤回流程");
        comment.setUserId(flowProcessInstance.getApplyBy());
        comment.setUsername(flowProcessInstance.getApplyByName());
        comment.setUserTaskId(flowProcessInstance.getId());
        comment.setTaskInstanceId(flowProcessInstance.getId());
        comment.setTaskInstanceId("end");
        comment.setTaskDefinitionId("end");
        comment.setProcessInstanceId(flowProcessInstance.getId());
        comment.setProcessDefinitionId(flowProcessInstance.getProcessDefinitionId());

        comment.setCommentType("widthdraw");
        comment.setCommentTypeName("撤回流程");
        comment.setFormId(flowProcessInstance.getFormId());
        return comment;
    }

    public static WithdrawEvent createWithdrawEvent(WithdrawCommand command) {
        WithdrawEvent event = new WithdrawEvent();
        event.setCommand(command);
        return event;
    }

    public static void setLogInfo(FlowProcessInstance flowProcessInstance, FlowActionLog log, FlowEngine flowEngine) {
        log.setProcessInstanceId(flowProcessInstance.getId());
        log.setProcessDefinitionId(flowProcessInstance.getProcessDefinitionId());
        FlowProcessDefinition flowProcessDefinition = flowEngine.getFlowProcessDefinitionService().getById(flowEngine.getFlowProcessDefinitionRefService().getById(flowProcessInstance.getProcessDefinitionId()).getModelId());
        log.setProcessDefinitionName(flowProcessDefinition.getFlowModelName());
        log.setFormId(flowProcessInstance.getFormId());
        log.setFormTypeId(flowProcessInstance.getFormTypeId());
        log.setProcessTitle(flowProcessInstance.getProcessTitle());
    }

    public static void setLogUserInfo(FlowActionLog log, FlowEngine flowEngine) {
        log.setUserId(flowEngine.getCurrentUser().getId());
        log.setUserName(flowEngine.getCurrentUser().getUsername());
    }

    public static void setLogInfo(FlowUserTask flowUserTask, FlowActionLog log, FlowEngine flowEngine) {
        FlowProcessInstance flowProcessInstance = flowEngine.getFlowProcessInstanceService().getById(flowUserTask.getProcessInstanceId());
        setLogInfo(flowProcessInstance, log, flowEngine);
        log.setTaskId(flowUserTask.getId());
        log.setTaskName(flowUserTask.getTaskName());
    }

    public static void setLogInfo(FlowUserTaskHistory flowUserTask, FlowProcessInstanceHistory flowProcessInstance, FlowActionLog log, FlowEngine flowEngine) {
        log.setProcessInstanceId(flowProcessInstance.getId());
        log.setProcessDefinitionId(flowProcessInstance.getProcessDefinitionId());
        FlowProcessDefinition flowProcessDefinition = flowEngine.getFlowProcessDefinitionService().getById(flowEngine.getFlowProcessDefinitionRefService().getById(flowProcessInstance.getProcessDefinitionId()).getModelId());
        log.setProcessDefinitionName(flowProcessDefinition.getFlowModelName());
        log.setFormId(flowProcessInstance.getFormId());
        log.setFormTypeId(flowProcessInstance.getFormTypeId());
        log.setProcessTitle(flowProcessInstance.getProcessTitle());
        log.setTaskId(flowUserTask.getId());
        log.setTaskName(flowUserTask.getTaskName());
    }

    public static FlowModelWalker walkerBegin(FlowModel flowModel, String nodeId) {
        FlowNode flowNode = flowModel.getNodeById(nodeId);
        List<FlowNode> beginNodes = CommonUtils.newList();
        if (!CommonUtils.isEmpty(flowNode)) {
            beginNodes.add(flowNode);
        }
        return new FlowModelWalker(beginNodes, flowModel);
    }
}
